﻿using System;
using System.IO;

namespace easysmb
{
    using easysmbrary;
    using easysmbrary.Client;

    public class EasySMBFileStream : Stream
    {
        internal EasySMBFile file { get; set; }
        internal long size;
        internal ISMBFileStore smb_tree { get; set; }
        internal object smb_file_handle { get; set; }
        internal bool enable_read = false;
        internal bool enable_write = false;
        internal EasySMBFileStream(EasySMBFile file)
        {
            this.file = file;
            this.size = this.file.FileSize;
            this.Position = 0;
        }
        internal void load_smbhandle()
        {
            var tree = file.smb.smb_client.TreeConnect(file.smb.read_sharename(file.FullPath), out var status);
            if (status != NTStatus.STATUS_SUCCESS) goto END;

            smb_tree = tree;
            END:;
        }
        internal void load_filehandle(FileAccess access, FileShare share)
        {
            if (smb_tree == null) load_smbhandle();
            if (smb_tree == null) goto END;

            var r = (access & FileAccess.Read) != 0;
            var w = (access & FileAccess.Write) != 0;
            AccessMask access_mask = 0;
            if (r) access_mask |= (AccessMask)FileAccessMask.FILE_READ_DATA;
            if (w) access_mask |= (AccessMask)FileAccessMask.FILE_WRITE_DATA;

            r = (share & FileShare.Read) != 0;
            w = (share & FileShare.Write) != 0;
            ShareAccess share_access = 0;
            if (r) share_access |= ShareAccess.Read;
            if (w) share_access |= ShareAccess.Write;

            var status = smb_tree.CreateFile(out object handle, out FileStatus fileStatus, file.smb.read_sharepath(file.FullPath), access_mask, 0, share_access, CreateDisposition.FILE_OPEN, 0, null);
            if (status != NTStatus.STATUS_SUCCESS) goto END;

            smb_file_handle = handle;
            this.enable_read = r;
            this.enable_write = w;
            END:;
        }
        public override bool CanRead { get => enable_read; }

        public override bool CanSeek { get => true; }

        public override bool CanWrite { get => enable_write; }

        public override long Length => size;

        public override long Position { get; set; }

        public override long Seek(long offset, SeekOrigin origin)
        {
            switch (origin)
            {
                case SeekOrigin.Begin:
                    this.Position = offset;
                    break;
                case SeekOrigin.Current:
                    this.Position += offset;
                    break;
                case SeekOrigin.End:
                    if (offset > 0) throw new ArgumentOutOfRangeException();
                    this.Position = size + offset;
                    break;
            }
            return this.Position;
        }

        public override void SetLength(long value)
        {
            if (!CanWrite) throw new NotSupportedException();
            var info_size = new FileEndOfFileInformation();
            info_size.EndOfFile = value;
            smb_tree.SetFileInformation(smb_file_handle, info_size);
            this.size = value;
            if (this.Position > this.size) this.Position = this.size;
        }
        public override void Flush()
        {
            if (!CanWrite) throw new NotSupportedException();
            smb_tree.FlushFileBuffers(smb_file_handle);
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (!CanWrite) throw new NotSupportedException();
            var buf = new byte[count];
            for (int i = 0, j = offset; i < count; i ++, j++) buf[i] = buffer[j];
            smb_tree.WriteFile(out var write_size, smb_file_handle, this.Position, buf);

            this.Position += write_size;
            if (this.Position > size) size = this.Position;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (this.Position == size) return 0;
            if (! CanRead) throw new NotSupportedException();
            if (count + this.Position > size) count = (int)(size - this.Position);
            smb_tree.ReadFile(out var read_size, buffer, offset, smb_file_handle, this.Position, count);
            this.Position += read_size;
            return read_size;
        }
        public override void Close()
        {
            base.Close();
            if (smb_file_handle != null) smb_tree.CloseFile(smb_file_handle);
            smb_file_handle = null;
            smb_tree = null;
        }
    }
}
